package com.jiangruan.javaproject.address;

import com.jiangruan.javaproject.tools.Base58;
import com.jiangruan.javaproject.tools.Hash;

import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.util.Arrays;

/**
 * @author:张子寒
 * @email:1594426628@qq.com
 * @desc:该类用于推导生成原始格式的比特币地址,并对比特币地址进行规范校验
 * @DateTime:2021-12-23
 **/
public class BitCoinAddress {
    //该方法用于生成密钥对，并返回密钥对信息
        public KeyPair generateKey(){
            try {
                KeyPairGenerator generator=KeyPairGenerator.getInstance("EC");
                ECGenParameterSpec spec=new ECGenParameterSpec("secp256k1");
                generator.initialize(spec);//初始化
                //生成密钥
                    return generator.generateKeyPair();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (InvalidAlgorithmParameterException e) {
                e.printStackTrace();
            }
            return null;
        }
        //该方法用于推导得到一个原始格式的地址，并将生成的地址返回   keyPair提供的密钥对
        public String newAddress(KeyPair keyPair){
            //公钥
           ECPublicKey publicKey=(ECPublicKey)keyPair.getPublic();
            byte[] pubkey = toUnCompressPubkey(publicKey);

            byte[] sha256 = Hash.SHA256(pubkey);
            byte[]  pubhash=Hash.RipeMD160(sha256);
            //对公钥hash前面添加版本号
            byte[] version=new byte[1];
            version[0]=0x00;
            byte[] ver_pubhash=new byte[version.length+pubhash.length];
            System.arraycopy(version,0,ver_pubhash,0,version.length);
            System.arraycopy(pubhash,0,ver_pubhash,version.length,pubhash.length);

            //对ver_pubhash进行双重256，取前四个字节
            byte[] hash1 = Hash.SHA256(ver_pubhash);
            byte[] hash2 = Hash.SHA256(hash1);

            byte[] check=new byte[4];
            System.arraycopy(hash2,0,check,0,check.length);

            //ver_pubhash_check
            byte[]ver_pubhash_check=new byte[ver_pubhash.length+check.length];
            System.arraycopy(ver_pubhash,0,ver_pubhash_check,0,ver_pubhash.length);
            System.arraycopy(check,0,ver_pubhash_check,ver_pubhash.length,check.length);
            //对ver_pubhash_check进行base58编码
            return Base58.encode(ver_pubhash_check);

        }
        //该方法用于返回比特币是否符合规范 address要校验的地址
        public boolean validateAddress(String address){
            if ("".equals(address)||address==null){
                return false;
            }
            byte[] ver_pubhash_check = Base58.decode(address);
            byte[]check=new byte[4];
            if (ver_pubhash_check.length<4){
                return false;
            }
            System.arraycopy(ver_pubhash_check,ver_pubhash_check.length-4,check,0,check.length);
            byte[]ver_pubhash=new byte[ver_pubhash_check.length-4];
            System.arraycopy(ver_pubhash_check,0,ver_pubhash,0,ver_pubhash.length);
            //双重256，取前四个字节
            byte[]hash1=Hash.SHA256(ver_pubhash);
            byte[]hash2=Hash.SHA256(hash1);
            //取前四个字节
            byte[]check_sum=new byte[4];
            System.arraycopy(hash2,0,check_sum,0,check_sum.length);
            return Arrays.equals(check, check_sum);
        }
        public byte[] toUnCompressPubkey(ECPublicKey publicKey){
            //对公钥进行hash计算
            //对原始公钥进行hash
            //原始公钥格式:65字节
            //04+x坐标(32字节)+y坐标（32字节）
            ECPoint point = publicKey.getW();
            byte[] xBytes = point.getAffineX().toByteArray();
            byte[] yBytes = point.getAffineY().toByteArray();

            //01+x+y
            byte[] pubkey=new byte[1+2*32];
            pubkey[0]=04;
            if (xBytes.length==32){
                System.arraycopy(xBytes,0,pubkey,1,xBytes.length);
            }else if (xBytes.length>32){
                System.arraycopy(xBytes,1,pubkey,1,32);
            }
            if (yBytes.length==32){
                System.arraycopy(yBytes,0,pubkey,33,yBytes.length);
            }else if (yBytes.length>32){
                System.arraycopy(yBytes,1,pubkey,33,32);
            }
            return pubkey;
        }
}
